﻿#region License
/*
Copyright (c) 2005-2008, CellAO Team

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of the CellAO Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

#region Usings...
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.CSharp;
#endregion

namespace AO.Core
{
    /// <summary>
    /// 
    /// </summary>
    public class Script_Compile
    {
		private static string _ENTRY_POINT = "ScriptMain"; //Assembly entry point
        /// <summary>
        /// 
        /// </summary>
		public static string DefaultScriptEntry
		{
			get { return _ENTRY_POINT; }
		}
		private static StringCollection assemblies;

		/// <summary>
        /// 
        /// </summary>
        public void Script()
        {
                CSharpCodeProvider ccp = new CSharpCodeProvider();
                string[] ReferenceAssemblies = { "System.dll" };
                string BuildName = Environment.CurrentDirectory + "/Scripts.dll";
                CompilerParameters cp = new CompilerParameters(ReferenceAssemblies, BuildName);
                cp.GenerateExecutable = false;
                cp.GenerateInMemory = false;
                DirectoryInfo scriptFiles = new DirectoryInfo(Environment.CurrentDirectory + "/Scripts/");        
        }

		/// <summary>
		/// Creates an assembly from a specified file name and returns the compiled code.
		/// Right now only in-memory-compiling, might wanna change it to .dll files later on to save compile times
		/// </summary>
		/// <param name="scriptFileName">path to the script file</param>
		/// <param name="useExtendedAssemblyReferences">toggles use of non-standard references such as AO.Core, Cell.Core and System. Edit code to add more</param>
		/// <returns>compiled assembly ready to use</returns>
		public static Assembly CreateAssembly(string scriptFileName, bool useExtendedAssemblyReferences)
		{
			if (!File.Exists(scriptFileName))
				throw new InvalidOperationException("Error: specified script file does not exist.");
			CompilerParameters cparams = new CompilerParameters();
			cparams.CompilerOptions = "/target:library /optimize";
			cparams.GenerateExecutable = false;
			cparams.GenerateInMemory = true;
			cparams.IncludeDebugInformation = false;
			assemblies = cparams.ReferencedAssemblies;
			assemblies.Add("AO.Core.ScriptAPI.DLL");
			assemblies.Add("System.DLL");
			//if (useExtendedAssemblyReferences)
			//{
			//    assemblies.Add("AO.Core.DLL");
			//    assemblies.Add("Cell.Core.DLL");
			//}

			CodeDomProvider compiler = new CSharpCodeProvider();
			CompilerResults cresults = compiler.CompileAssemblyFromFile(cparams, new string[] { scriptFileName });
			if (cresults.Errors.Count > 0)
			{
				foreach (CompilerError error in cresults.Errors)
					Console.WriteLine("Compiler error in {0} (line {1}):\n{2}", error.FileName, error.Line, error.ErrorText);
				return null;
			}

			return cresults.CompiledAssembly;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="assembly"></param>
		/// <param name="entryPoint"></param>
		public static void CallEntry(Assembly assembly, string entryPoint)
		{
			try
			{
				Module[] mods = assembly.GetModules(false);
				Type[] types = mods[0].GetTypes();

				foreach (Type type in types)
				{
					BindingFlags flgs = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
					MethodInfo mi = type.GetMethod(entryPoint, flgs);
					if (mi != null)
					{
						if (mi.GetParameters().Length == 1)
						{
							if (mi.GetParameters()[0].ParameterType.IsArray)
							{
								string[] par = new string[1]; // if entry method has string [] arguments
								mi.Invoke(null, par);
							}
						}
						else
							mi.Invoke(null, null);
						return;
					}
				}
				Console.WriteLine("Engine could not find the public static {0}." + entryPoint);
			}
			catch (Exception ex)
			{
				Console.WriteLine("Error: An exception occurred: {0}", ex);
			}
		}
	}
}
